{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "8db4ada5ce6ebf73",
   "metadata": {},
   "source": [
    "### 低开销编译器\n",
    "DEAP在编译GP时使用了Python的默认编译器，但是Python默认编译器在编译GP时实际上速度较慢，因此我们可以考虑自行实现一个编译器来加速GP运算。更严格来说，应该是自行实现一个GP树的解析函数，从而降低编译的时间开销。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "initial_id",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-25T02:37:08.047926400Z",
     "start_time": "2023-12-25T02:37:08.020758200Z"
    }
   },
   "outputs": [],
   "source": [
    "import time\n",
    "import warnings\n",
    "\n",
    "import numpy as np\n",
    "from deap import base, creator, tools, gp\n",
    "from deap.gp import PrimitiveTree, Primitive, Terminal\n",
    "\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "\n",
    "\n",
    "def quick_evaluate(expr: PrimitiveTree, pset, data, prefix='ARG'):\n",
    "    result = None\n",
    "    stack = []\n",
    "    for node in expr:\n",
    "        stack.append((node, []))\n",
    "        while len(stack[-1][1]) == stack[-1][0].arity:\n",
    "            prim, args = stack.pop()\n",
    "            if isinstance(prim, Primitive):\n",
    "                result = pset.context[prim.name](*args)\n",
    "            elif isinstance(prim, Terminal):\n",
    "                if prefix in prim.name:\n",
    "                    result = data[:, int(prim.name.replace(prefix, ''))]\n",
    "                else:\n",
    "                    result = prim.value\n",
    "            else:\n",
    "                raise Exception\n",
    "            if len(stack) == 0:\n",
    "                break  # 栈为空代表所有节点都已经被访问\n",
    "            stack[-1][1].append(result)\n",
    "    return result\n",
    "\n",
    "\n",
    "# 符号回归\n",
    "def evalSymbReg(individual, pset):\n",
    "    # 使用numpy创建一个向量\n",
    "    x = np.linspace(-10, 10, 100).reshape(-1, 1)\n",
    "\n",
    "    # 评估生成的函数并计算MSE\n",
    "    mse = np.mean((quick_evaluate(individual, pset, x) - x ** 2) ** 2)\n",
    "\n",
    "    return (mse,)\n",
    "\n",
    "\n",
    "# 创建个体和适应度函数\n",
    "creator.create(\"FitnessMin\", base.Fitness, weights=(-1.0,))\n",
    "creator.create(\"Individual\", gp.PrimitiveTree, fitness=creator.FitnessMin)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3d94e424b58af5a",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "cb6cf38094256262",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-25T02:37:08.054440500Z",
     "start_time": "2023-12-25T02:37:08.049951400Z"
    }
   },
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "# 定义函数集合和终端集合\n",
    "pset = gp.PrimitiveSet(\"MAIN\", arity=1)\n",
    "pset.addPrimitive(np.add, 2)\n",
    "pset.addPrimitive(np.subtract, 2)\n",
    "pset.addPrimitive(np.multiply, 2)\n",
    "pset.addPrimitive(np.negative, 1)\n",
    "pset.addEphemeralConstant(\"rand101\", lambda: random.randint(-1, 1))\n",
    "\n",
    "# 定义遗传编程操作\n",
    "toolbox = base.Toolbox()\n",
    "toolbox.register(\"expr\", gp.genHalfAndHalf, pset=pset, min_=1, max_=2)\n",
    "toolbox.register(\"individual\", tools.initIterate, creator.Individual, toolbox.expr)\n",
    "toolbox.register(\"population\", tools.initRepeat, list, toolbox.individual)\n",
    "toolbox.register(\"compile\", gp.compile, pset=pset)\n",
    "toolbox.register(\"evaluate\", evalSymbReg, pset=pset)\n",
    "toolbox.register(\"select\", tools.selTournament, tournsize=3)\n",
    "toolbox.register(\"mate\", gp.cxOnePoint)\n",
    "toolbox.register(\"mutate\", gp.mutUniform, expr=toolbox.expr, pset=pset)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e09fa8e7890d583b",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "88c62bc071d56191",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-25T02:37:08.226956400Z",
     "start_time": "2023-12-25T02:37:08.054649800Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   \t      \t                          fitness                          \t                      size                     \n",
      "   \t      \t-----------------------------------------------------------\t-----------------------------------------------\n",
      "gen\tnevals\tavg   \tgen\tmax        \tmin    \tnevals\tstd        \tavg \tgen\tmax\tmin\tnevals\tstd    \n",
      "0  \t100   \t121165\t0  \t1.18875e+07\t1849.78\t100   \t1.18256e+06\t3.88\t0  \t7  \t2  \t100   \t1.45794\n",
      "1  \t92    \t5608.85\t1  \t187844     \t1849.78\t92    \t23462.3    \t4.2 \t1  \t13 \t2  \t92    \t1.92873\n",
      "2  \t91    \t5064.47\t2  \t153712     \t1849.78\t91    \t21235.6    \t4.42\t2  \t10 \t2  \t91    \t1.69222\n",
      "3  \t92    \t11535  \t3  \t608604     \t1849.78\t92    \t63855.3    \t4.92\t3  \t11 \t2  \t92    \t1.91144\n",
      "4  \t86    \t239906 \t4  \t1.18875e+07\t1849.78\t86    \t1.66394e+06\t5.08\t4  \t11 \t2  \t86    \t2.00838\n",
      "5  \t88    \t15739.1\t5  \t157411     \t1849.78\t88    \t43218.6    \t5.35\t5  \t11 \t2  \t88    \t2.20624\n",
      "6  \t86    \t125529 \t6  \t1.21905e+07\t1849.78\t86    \t1.21267e+06\t5.42\t6  \t11 \t2  \t86    \t2.31594\n",
      "7  \t92    \t5316.53\t7  \t153712     \t1849.78\t92    \t21216      \t4.68\t7  \t12 \t2  \t92    \t2.08269\n",
      "8  \t88    \t5193.51\t8  \t157909     \t1849.78\t88    \t21522.5    \t4.33\t8  \t11 \t3  \t88    \t1.94964\n",
      "9  \t91    \t5205.65\t9  \t153712     \t1849.78\t91    \t21222.2    \t4.79\t9  \t13 \t3  \t91    \t2.7615 \n",
      "10 \t85    \t9633.91\t10 \t153712     \t1849.78\t85    \t33032.6    \t5.18\t10 \t13 \t2  \t85    \t3.18867\n",
      "time: 0.05787515640258789\n",
      "subtract(ARG0, subtract(ARG0, multiply(ARG0, ARG0)))\n",
      "   \t      \t                    fitness                    \t                      size                     \n",
      "   \t      \t-----------------------------------------------\t-----------------------------------------------\n",
      "gen\tnevals\tavg    \tgen\tmax    \tmin    \tnevals\tstd    \tavg \tgen\tmax\tmin\tnevals\tstd    \n",
      "0  \t100   \t2399.54\t0  \t15032.6\t1849.78\t100   \t1535.98\t4.12\t0  \t7  \t2  \t100   \t1.83456\n",
      "1  \t93    \t8232.16\t1  \t608604 \t1849.78\t93    \t60342.8\t3.89\t1  \t7  \t2  \t93    \t1.68461\n",
      "2  \t91    \t2210.27\t2  \t6509.62\t1849.78\t91    \t838.323\t4.39\t2  \t11 \t2  \t91    \t1.85954\n",
      "3  \t94    \t3762.19\t3  \t153712 \t1849.78\t94    \t15099.3\t4.86\t3  \t16 \t2  \t94    \t2.33247\n",
      "4  \t98    \t9841.9 \t4  \t153712 \t1849.78\t98    \t33020.9\t5.34\t4  \t13 \t2  \t98    \t2.5582 \n",
      "5  \t91    \t16268.1\t5  \t153712 \t1849.78\t91    \t43253.7\t5.48\t5  \t13 \t2  \t91    \t2.11887\n",
      "6  \t91    \t8519.32\t6  \t158106 \t1849.78\t91    \t29888.1\t5.45\t6  \t17 \t2  \t91    \t2.35531\n",
      "7  \t91    \t9997.53\t7  \t162168 \t1849.78\t91    \t33586.1\t5.59\t7  \t13 \t2  \t91    \t2.58494\n",
      "8  \t90    \t8444.5 \t8  \t166663 \t1849.78\t90    \t30572.6\t4.96\t8  \t13 \t3  \t90    \t2.25353\n",
      "9  \t92    \t6848.75\t9  \t153712 \t1849.78\t92    \t25881.5\t4.43\t9  \t11 \t3  \t92    \t1.92486\n",
      "10 \t92    \t3698   \t10 \t153712 \t1849.78\t92    \t15148.2\t4.15\t10 \t14 \t2  \t92    \t2.03162\n",
      "time: 0.04912590980529785\n",
      "add(multiply(ARG0, add(-1, ARG0)), ARG0)\n",
      "   \t      \t                    fitness                    \t                      size                     \n",
      "   \t      \t-----------------------------------------------\t-----------------------------------------------\n",
      "gen\tnevals\tavg    \tgen\tmax    \tmin    \tnevals\tstd    \tavg\tgen\tmax\tmin\tnevals\tstd    \n",
      "0  \t100   \t2177.79\t0  \t6611.64\t1849.78\t100   \t630.853\t4  \t0  \t7  \t2  \t100   \t1.76068\n",
      "1  \t95    \t2189.79\t1  \t6612.64\t1849.78\t95    \t728.969\t4.1\t1  \t12 \t2  \t95    \t1.9105 \n",
      "2  \t98    \t9804.71\t2  \t166663 \t1849.78\t98    \t33450.6\t4.51\t2  \t12 \t2  \t98    \t2.3345 \n",
      "3  \t96    \t8355.08\t3  \t158106 \t1849.78\t96    \t29706.7\t4.69\t3  \t18 \t2  \t96    \t2.26581\n",
      "4  \t87    \t125529 \t4  \t1.20391e+07\t1849.78\t87    \t1.19755e+06\t4.43\t4  \t13 \t3  \t87    \t1.81799\n",
      "5  \t96    \t11254  \t5  \t153712     \t1849.78\t96    \t36004.4    \t4.29\t5  \t10 \t3  \t96    \t1.57032\n",
      "6  \t89    \t6829.83\t6  \t158106     \t1849.78\t89    \t26103.9    \t3.9 \t6  \t10 \t3  \t89    \t1.50665\n",
      "7  \t92    \t5291.95\t7  \t153712     \t1849.78\t92    \t21221.9    \t4.04\t7  \t10 \t3  \t92    \t1.77719\n",
      "8  \t94    \t8262.71\t8  \t153712     \t1849.78\t94    \t29719.3    \t3.75\t8  \t13 \t2  \t94    \t1.63325\n",
      "9  \t86    \t6564.08\t9  \t153712     \t1849.78\t86    \t25884      \t3.76\t9  \t8  \t2  \t86    \t1.40086\n",
      "10 \t90    \t9918.84\t10 \t158106     \t1849.78\t90    \t33185.9    \t3.92\t10 \t12 \t2  \t90    \t1.74172\n",
      "time: 0.050930023193359375\n",
      "subtract(ARG0, subtract(ARG0, multiply(ARG0, ARG0)))\n"
     ]
    }
   ],
   "source": [
    "import numpy\n",
    "from deap import algorithms\n",
    "\n",
    "# 定义统计指标\n",
    "stats_fit = tools.Statistics(lambda ind: ind.fitness.values)\n",
    "stats_size = tools.Statistics(len)\n",
    "mstats = tools.MultiStatistics(fitness=stats_fit, size=stats_size)\n",
    "mstats.register(\"avg\", numpy.mean)\n",
    "mstats.register(\"std\", numpy.std)\n",
    "mstats.register(\"min\", numpy.min)\n",
    "mstats.register(\"max\", numpy.max)\n",
    "\n",
    "# 使用默认算法\n",
    "custom_compiler_time = []\n",
    "for i in range(3):\n",
    "    start = time.time()\n",
    "    population = toolbox.population(n=100)\n",
    "    hof = tools.HallOfFame(1)\n",
    "    pop, log = algorithms.eaSimple(population=population,\n",
    "                                   toolbox=toolbox, cxpb=0.9, mutpb=0.1, ngen=10, stats=mstats, halloffame=hof,\n",
    "                                   verbose=True)\n",
    "    end = time.time()\n",
    "    print('time:', end - start)\n",
    "    print(str(hof[0]))\n",
    "    custom_compiler_time.append(end - start)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "93e26be78cb63ef",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-25T02:37:08.449160200Z",
     "start_time": "2023-12-25T02:37:08.228958300Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   \t      \t                          fitness                          \t                      size                     \n",
      "   \t      \t-----------------------------------------------------------\t-----------------------------------------------\n",
      "gen\tnevals\tavg   \tgen\tmax        \tmin\tnevals\tstd        \tavg\tgen\tmax\tmin\tnevals\tstd    \n",
      "0  \t100   \t119447\t0  \t1.17257e+07\t0  \t100   \t1.16648e+06\t4.2\t0  \t7  \t2  \t100   \t1.73781\n",
      "1  \t94    \t1603.85\t1  \t2387.41    \t0  \t94    \t878.039    \t4.53\t1  \t12 \t2  \t94    \t2.01224\n",
      "2  \t94    \t4695.69\t2  \t153712     \t0  \t94    \t21352.2    \t4.67\t2  \t9  \t2  \t94    \t1.93419\n",
      "3  \t94    \t10059.5\t3  \t157909     \t0  \t94    \t36398.7    \t4.46\t3  \t11 \t2  \t94    \t2.16527\n",
      "4  \t90    \t5346.64\t4  \t159956     \t0  \t90    \t26380.6    \t4.66\t4  \t11 \t3  \t90    \t2.24597\n",
      "5  \t90    \t12550.5\t5  \t153712     \t0  \t90    \t41327.4    \t4.44\t5  \t9  \t2  \t90    \t2.11339\n",
      "6  \t95    \t11218.5\t6  \t153712     \t0  \t95    \t38956.6    \t4.07\t6  \t9  \t3  \t95    \t1.79586\n",
      "7  \t93    \t123617 \t7  \t1.23323e+07\t0  \t93    \t1.22702e+06\t3.71\t7  \t9  \t3  \t93    \t1.40211\n",
      "8  \t92    \t6317.41\t8  \t608604     \t0  \t92    \t60539.9    \t3.5 \t8  \t9  \t3  \t92    \t1.36748\n",
      "9  \t78    \t8061.6 \t9  \t625254     \t0  \t78    \t63889      \t3.56\t9  \t9  \t2  \t78    \t1.45822\n",
      "10 \t90    \t6442.59\t10 \t159956     \t0  \t90    \t30400.3    \t3.47\t10 \t9  \t3  \t90    \t1.25264\n",
      "time: 0.0755617618560791\n",
      "   \t      \t                    fitness                    \t                      size                     \n",
      "   \t      \t-----------------------------------------------\t-----------------------------------------------\n",
      "gen\tnevals\tavg    \tgen\tmax    \tmin\tnevals\tstd    \tavg \tgen\tmax\tmin\tnevals\tstd    \n",
      "0  \t100   \t2029.52\t0  \t2387.41\t0  \t100   \t419.475\t3.89\t0  \t7  \t2  \t100   \t1.63643\n",
      "1  \t89    \t2104.04\t1  \t8325.39\t0  \t89    \t1232.12\t3.78\t1  \t7  \t2  \t89    \t1.37535\n",
      "2  \t84    \t3713.2 \t2  \t153712 \t0  \t84    \t15152.6\t4.25\t2  \t10 \t2  \t84    \t1.55161\n",
      "3  \t88    \t122148 \t3  \t1.17257e+07\t0  \t88    \t1.16639e+06\t4.85\t3  \t11 \t2  \t88    \t1.87283\n",
      "4  \t83    \t3606.61\t4  \t153712     \t0  \t83    \t15236.2    \t5.34\t4  \t15 \t2  \t83    \t2.201  \n",
      "5  \t90    \t7938.61\t5  \t606522     \t0  \t90    \t60184.1    \t5.52\t5  \t12 \t2  \t90    \t2.147  \n",
      "6  \t95    \t3180.84\t6  \t153712     \t0  \t95    \t15201      \t5.41\t6  \t12 \t2  \t95    \t2.27198\n",
      "7  \t90    \t2697.73\t7  \t151631     \t0  \t90    \t15019.6    \t4.72\t7  \t11 \t2  \t90    \t2.18211\n",
      "8  \t87    \t2655.63\t8  \t159956     \t0  \t87    \t15989.2    \t4.56\t8  \t11 \t3  \t87    \t2.20599\n",
      "9  \t88    \t542.04 \t9  \t8359.39    \t0  \t88    \t1162.05    \t4.22\t9  \t11 \t3  \t88    \t1.83074\n",
      "10 \t96    \t6420.42\t10 \t608604     \t0  \t96    \t60530.8    \t3.69\t10 \t9  \t3  \t96    \t1.27824\n",
      "time: 0.0690147876739502\n",
      "   \t      \t                    fitness                    \t                      size                     \n",
      "   \t      \t-----------------------------------------------\t-----------------------------------------------\n",
      "gen\tnevals\tavg    \tgen\tmax   \tmin\tnevals\tstd    \tavg\tgen\tmax\tmin\tnevals\tstd    \n",
      "0  \t100   \t12912.8\t0  \t608604\t0  \t100   \t65507.4\t4  \t0  \t7  \t2  \t100   \t1.69115\n",
      "1  \t94    \t1839.74\t1  \t8224.37\t0  \t94    \t985.792\t4.02\t1  \t9  \t2  \t94    \t1.81648\n",
      "2  \t88    \t2949.82\t2  \t151631 \t0  \t88    \t14990.5\t4.12\t2  \t10 \t2  \t88    \t1.85084\n",
      "3  \t84    \t4156.77\t3  \t159956 \t0  \t84    \t21851.6\t4.11\t3  \t11 \t2  \t84    \t2.02926\n",
      "4  \t94    \t2167.67\t4  \t153712 \t0  \t94    \t15294.9\t3.74\t4  \t9  \t3  \t94    \t1.41152\n",
      "5  \t87    \t1789.28\t5  \t145523 \t0  \t87    \t14483.8\t3.48\t5  \t9  \t2  \t87    \t1.20399\n",
      "6  \t89    \t190.092\t6  \t8325.39\t0  \t89    \t939.729\t3.26\t6  \t7  \t3  \t89    \t0.743236\n",
      "7  \t93    \t190.782\t7  \t8325.39\t0  \t93    \t940.954\t3.38\t7  \t9  \t3  \t93    \t1.08425 \n",
      "8  \t92    \t544.571\t8  \t8359.39\t0  \t92    \t1857.96\t3.36\t8  \t9  \t3  \t92    \t1.07257 \n",
      "9  \t96    \t168.908\t9  \t2115.35\t0  \t96    \t561.94 \t3.32\t9  \t9  \t2  \t96    \t1.06658 \n",
      "10 \t86    \t499.543\t10 \t18732.1\t0  \t86    \t2224.64\t3.41\t10 \t9  \t3  \t86    \t1.19243 \n",
      "time: 0.07066202163696289\n"
     ]
    }
   ],
   "source": [
    "# 慢速评估\n",
    "def evalSymbReg(individual, pset):\n",
    "    # 编译GP树为函数\n",
    "    func = gp.compile(expr=individual, pset=pset)\n",
    "\n",
    "    # 使用numpy创建一个向量\n",
    "    x = np.linspace(-10, 10, 100)\n",
    "\n",
    "    # 评估生成的函数并计算MSE\n",
    "    mse = np.mean((func(x) - x ** 2) ** 2)\n",
    "\n",
    "    return (mse,)\n",
    "\n",
    "\n",
    "toolbox.register(\"evaluate\", evalSymbReg, pset=pset)\n",
    "\n",
    "py_time = []\n",
    "for i in range(3):\n",
    "    start = time.time()\n",
    "    population = toolbox.population(n=100)\n",
    "    hof = tools.HallOfFame(1)\n",
    "    pop, log = algorithms.eaSimple(population=population,\n",
    "                                   toolbox=toolbox, cxpb=0.9, mutpb=0.1, ngen=10, stats=mstats, halloffame=hof,\n",
    "                                   verbose=True)\n",
    "    end = time.time()\n",
    "    print('time:', end - start)\n",
    "    py_time.append(end - start)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c746d5032e852bb",
   "metadata": {},
   "source": [
    "下图展示了实验结果，从实验结果可以看出，自行实现的编译器在编译GP树时的速度要快于Python默认编译器。主要是因为自行实现的编译器基本没有额外开销，而Python默认编译器在编译时会进行一些额外的操作，因此速度较慢。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c6e9fb08c172a7f0",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-25T02:37:08.749483500Z",
     "start_time": "2023-12-25T02:37:08.450148200Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "<Figure size 400x300 with 1 Axes>",
      "image/png": "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EekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdhhsREekdY103gF6sosJCXTdBa6rSshJR6RhuekgIIf1/bse3OmyJ7jy6Doio6mG3JBER6R0euekhAwMD6f9WIwfB0LhqvM1FhYXSkeqj64CIqp6qsderwgyNjWFowreZiKoWnXRLpqenw9fXF66urnB3d8eyZctQ+ISLAeLi4jBw4EA4OzujX79+OHLkiDSsqKgISqUSzs7OUCqV0l92djYAIDs7G8HBwXB3d4eLiwtmz56NrKwsrSwjERHpjk7CLSAgAJaWloiPj8euXbtw4sQJbNmypVi9S5cuwc/PDzNmzMCpU6fg5+eHgIAA3Lx5EwCQnJyMgoICqFQqJCYmSn+WlpYAgCVLluD69es4cOAADh48iOvXryMkJESbi0pERDqg9XC7fPkyVCoVZs2aBQsLCzRq1Ai+vr6IiooqVnfPnj1wdXVFr169YGxsjP79+8PNzQ07duwAACQlJcHe3h6mpqbFxs3JycG+ffvg7+8Pa2tr1K5dGzNnzkR0dDRycnJe+HJWFkWFhSgq0O6fOr8A6vwCrc+XtwIQkYbWT8ZcuHAB1tbWqFu3rlTWvHlzXLt2Dffv38crr7wilScnJ0OhUMjGb9GiBc6dOwfgYbjl5eVh2LBhuHr1Kpo3b473338f7du3x+XLl1FQUCAbv3nz5sjNzcWlS5fQunXrMrdZrVZXdHF14tH2VtVbAdRq9Uv3vhFpQ1X5XGg93LKysmBhYSEr07zOzs6WhVtJdc3NzaVzaubm5nBycsKMGTNQo0YNREVFYdKkSYiJiUFmZiYASF2Uj86nvOfdkpKSylVf1woKCnTdBJ37448/YGJioutmEJGOaD3cLC0ti3ULal5bWVnJyi0sLJCbmysry83NleoFBQXJhk2aNAnR0dGIi4tD+/btpWlr6mvmU61atXK12dHREUZGRuUaR5eEEHByctLJvPPz8zFv3jwAwLJly0rsMtYGU1NT3g5AVAK1Wv3SfWGvCK2HW8uWLXH37l2kpaXBxsYGAJCSkgJbW1tUr15dVlehUODs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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import seaborn as sns\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd\n",
    "\n",
    "data = pd.DataFrame(\n",
    "    {'Category': ['Efficient Compiler'] * len(custom_compiler_time) + ['Python Compiler'] * len(py_time),\n",
    "     'Time': np.concatenate([custom_compiler_time, py_time])})\n",
    "\n",
    "plt.figure(figsize=(4, 3))\n",
    "sns.set_style(\"whitegrid\")\n",
    "sns.boxplot(data=data, x='Category', y='Time', palette=\"Set3\", width=0.4)\n",
    "plt.title('Comparison of Efficient Compiler and Python Compiler')\n",
    "plt.xlabel('')\n",
    "plt.ylabel('Time')\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
